<?php

namespace addons\yungift\controller;

use addons\yungift\library\Constant;
use addons\yungift\library\Logic;
use addons\yungift\library\Super;
use addons\yungift\model\Activity;
use addons\yungift\model\Config;
use addons\yungift\model\Member;
use addons\yungift\model\MemberLog;
use addons\yungift\model\SmsCode;
use addons\yungift\model\UserAuth;
use \app\common\model\User as UserModel;
use think\Validate;

/**
 * 用户接口
 */
class User extends BaseApi
{
    protected $noNeedLogin = [
        'wechatAutoLogin',
        'mpAutologin',
        'loginByNameCode',
        'loginByNumberCode',
        'loginByPhoneCaptcha',
        'loginByWechatPhone',
        'sendPhoneCaptcha'
    ];

    /**
     * 微信小程序code登陆
     */
    public function wechatAutologin()
    {
        $wxCode = $this->request->post('wx_code');
        if (! $wxCodeData = Logic::getMiniCodeData($wxCode, false)) {
            $this->success('认证失败', null, Constant::IGNORE_ERROR);
        }
        $this->tryUserAuthLogin($wxCodeData['openid'], UserAuth::SOURCE_MINI);
        $this->success('操作成功', null, Constant::IGNORE_ERROR);
    }

    /**
     * 微信网页token登陆
     */
    public function mpAutologin()
    {
        $authKey = $this->request->post('auth_key');
        if (! $openid = Logic::getUserAuthOpenid($authKey, UserAuth::SOURCE_MP)) {
            $this->success('认证失败', null, Constant::IGNORE_ERROR);
        }
        $this->tryUserAuthLogin($openid, UserAuth::SOURCE_MP);
        $this->success('操作成功');
    }

    /**
     * 登录方式 - 姓名登录码
     */
    public function loginByNameCode()
    {
        $data = $this->request->post();
        $validate = new Validate(['name' => 'require', 'code' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $wxCode = $this->request->post('wx_code');
        $authKey = $this->request->post('auth_key');

        // 检查登录方式
        $this->checkLoginMethod('name_code');

        // 检查微信code
        $openid = $source = null;
        $wxCodeData = $this->checkWxCode($wxCode);
        if ($wxCodeData) {
            $source = UserAuth::SOURCE_MINI;
            $openid = $wxCodeData['openid'];
        } elseif ($authKey) {
            $openid = Logic::getUserAuthOpenid($authKey, UserAuth::SOURCE_MP);
            $source = UserAuth::SOURCE_MP;
        }

        // 查找用户
        $memberList = Member::where(['name' => $data['name'], 'code' => md5($data['code'])])->select();
        if (count($memberList) == 0) {
            $this->error(__('找不到当前姓名和登录码关联的用户'));
        }
        if (count($memberList) > 1) {
            $this->error(__('当前姓名和登录码关联的用户不止一个，请联系管理员处理'));
        }
        $member = $memberList[0];

        $this->tryMemberLogin($member, $openid, $source, 'name_code');
    }

    /**
     * 登录方式 - 编号登录码
     */
    public function loginByNumberCode()
    {
        $data = $this->request->post();
        $validate = new Validate(['number' => 'require', 'code' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $wxCode = $this->request->post('wx_code');
        $authKey = $this->request->post('auth_key');

        // 检查登录方式
        $this->checkLoginMethod('number_code');

        // 检查微信code
        $openid = $source = null;
        $wxCodeData = $this->checkWxCode($wxCode);
        if ($wxCodeData) {
            $source = UserAuth::SOURCE_MINI;
            $openid = $wxCodeData['openid'];
        } elseif ($authKey) {
            $openid = Logic::getUserAuthOpenid($authKey, UserAuth::SOURCE_MP);
            $source = UserAuth::SOURCE_MP;
        }

        // 查找用户
        $memberList = Member::where(['number' => $data['number'], 'code' => md5($data['code'])])->select();
        if (count($memberList) == 0) {
            $this->error(__('找不到当前编号和登录码关联的用户'));
        }
        if (count($memberList) > 1) {
            $this->error(__('当前编号和登录码关联的用户不止一个，请联系管理员处理'));
        }
        $member = $memberList[0];

        $this->tryMemberLogin($member, $openid, $source, 'number_code');
    }

    /**
     * 登录方式 - 手机验证码
     */
    public function loginByPhoneCaptcha()
    {
        $data = $this->request->post();
        $validate = new Validate(['phone' => 'require', 'captcha' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $wxCode = $this->request->post('wx_code');
        $authKey = $this->request->post('auth_key');
        $phone = trim($data['phone']);

        // 检查登录方式
        $this->checkLoginMethod('phone_captcha');

        // 检查微信code
        $openid = $source = null;
        $wxCodeData = $this->checkWxCode($wxCode);
        if ($wxCodeData) {
            $source = UserAuth::SOURCE_MINI;
            $openid = $wxCodeData['openid'];
        } elseif ($authKey) {
            $openid = Logic::getUserAuthOpenid($authKey, UserAuth::SOURCE_MP);
            $source = UserAuth::SOURCE_MP;
        }

        // 校验验证码
        $smsCode = SmsCode::where([
            'phone' => $phone,
            'code'  => trim($data['captcha'])
        ])->order(['created_at' => SORT_DESC])->find();
        if (! $smsCode) {
            $this->error(__('验证码错误'));
        }
        if ($smsCode->expired_at < time() || $smsCode->status != SmsCode::STATE_ING) {
            $this->error(__('验证码失效'));
        }
        $smsCode->status = SmsCode::STATE_END;
        $smsCode->save();

        // 查找用户
        $memberList = Member::where(['phone' => $phone])->select();
        if (count($memberList) == 0) {
            $this->error(__('找不到当前手机号关联的用户'));
        }
        if (count($memberList) > 1) {
            $this->error(__('当前手机号关联的用户不止一个，请联系管理员处理'));
        }
        $member = $memberList[0];

        $this->tryMemberLogin($member, $openid, $source, 'phone_captcha');
    }

    /**
     * 登录方式 - 微信手机号
     */
    public function loginByWechatPhone()
    {
        $data = $this->request->post();
        $validate = new Validate(['encrypted_data' => 'require', 'iv' => 'require', 'wx_code' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $encryptedData = $this->request->post("encrypted_data", '', 'trim');;
        $iv = $data['iv'];
        $wxCode = $data['wx_code'];

        // 检查登录方式
        $this->checkLoginMethod('wechat_phone');

        // 获取微信code
        if (! $wxCodeData = Logic::getMiniCodeData($wxCode)) {
            $this->error(__('获取微信登录信息失败'));
        }

        // 解密手机号
        if ($phoneCode = ($data['code'] ?? null)) {
            if (! $result = Logic::getWechatPhoneData($phoneCode)) {
                $this->error(__('获取微信手机号失败'));
            }
        } else {
            if (! $result = Logic::decryptWechatData($wxCodeData['session_key'], $encryptedData, $iv)) {
                $this->error(__('获取微信手机号失败'));
            }
        }

        // 查找用户
        $memberList = Member::where(['phone' => $result['phoneNumber']])->select();
        if (count($memberList) == 0) {
            $this->error(__('找不到当前手机号关联的用户'));
        }
        if (count($memberList) > 1) {
            $this->error(__('当前手机号关联的用户不止一个，请联系管理员处理'));
        }
        $member = $memberList[0];

        $this->tryMemberLogin($member, $wxCodeData['openid'], UserAuth::SOURCE_MINI, 'wechat_phone');
    }

    /**
     * 发送手机验证码
     */
    public function sendPhoneCaptcha()
    {
        $data = $this->request->post();
        $validate = new Validate(['phone' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $phone = trim($data['phone']);

        // 检查登录方式
        $this->checkLoginMethod('phone_captcha');

        // 检查微信code
        $wxCodeData = $this->checkWxCode($data['wx_code'] ?? null);

        // 增强校验：只给成员用户发验证码
        if (! $this->auth->isLogin()) {
            // 查找用户
            $memberList = Member::where(['phone' => $phone])->select();
            if (count($memberList) == 0) {
                $this->error(__('找不到当前手机号关联的用户'));
            }
            if (count($memberList) > 1) {
                $this->error(__('当前手机号关联的用户不止一个，请联系管理员处理'));
            }
            $member = $memberList[0];
            if ($member->status != Member::STATUS_NORMAL) {
                $this->error(__('当前用户被禁用，请联系管理员处理'));
            }
        } else {
            $memberIds = Member::where(['phone' => $phone])->column('id');
            if ($memberIds && array_diff($memberIds, [$this->memberId])) {
                $this->error(__('手机号已被占用，请重新换一个试试'));
            }
        }

        // 校验手机号发送验证码次数
        $recordList = SmsCode::where(['phone' => $phone])
            ->where('created_at', '>=', strtotime('today'))
            ->order(['created_at' => SORT_DESC])->select();
        if (count($recordList) > 0) {
            if ($recordList[0]->created_at > time() - 60) {
                $this->error(__('一分钟之内只能发送一次验证码'));
            }
        }
        if (count($recordList) >= 10) {
            $this->error(__('今日验证码发送次数已达上限'));
        }

        // 微信用户发送验证码次数限制
        if ($wxCodeData) {
            $userCount = SmsCode::where(['openid' => $wxCodeData['openid']])
                ->where('created_at', '>=', strtotime('today'))->count();
            if ($userCount >= 20) {
                $this->error(__('今日验证码发送次数已达上限'));
            }
        }

        // 保存验证码
        $code = random_int(100000, 999999);
        $smsCode = new SmsCode();
        $smsCode->openid = $wxCodeData['openid'] ?? null;
        $smsCode->phone = $phone;
        $smsCode->code = $code;
        $smsCode->status = SmsCode::STATE_ING;
        $smsCode->expired_at = time() + 300;
        $smsCode->save();

        if ($error = Logic::sendPhoneCaptcha($phone, $code)) {
            $this->error(__('短信发送失败: ' . $error));
        }

        $this->success('操作成功', null);
    }

    /**
     * 退出登录
     */
    public function logout()
    {
        MemberLog::quickLog(MemberLog::ACTION_LOGOUT, $this->memberId);  // 记录日志
        Member::update(['user_id' => 0], ['id' => $this->memberId]);
        $this->auth->logout();
        $this->success(__('退出成功'));
    }

    /**
     * 更新用户信息
     */
    public function updateUserInfo()
    {
        $update = [];

        // 修改头像
        if ($avatarUrl = $this->request->post('avatar_url')) {
            $update['avatar_url'] = $avatarUrl;
        }

        // 修改数据
        if ($update) {
            Member::update($update, ['id' => $this->memberId]);
            MemberLog::quickLog(MemberLog::ACTION_UPDATE_INFO, $this->memberId);  // 记录日志
        }

        $this->success(__('成功'));
    }

    /**
     * 获取用户信息
     */
    public function getUserInfo()
    {
        $result = Super::arrayGetSet($this->memberData, [
            'name',
            'number',
            'group_name' => function ($item) {
                return $item->group_text;
            },
            'nickname',
            'avatar_url',
            'phone'
        ]);

        $this->success(__('查询成功'), $result);
    }

    /**
     * 修改登录码
     */
    public function changeCode()
    {
        $data = $this->request->post();
        $validate = new Validate(['oldCode' => 'require', 'newCode' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $oldCode = trim($data['oldCode']);
        $newCode = trim($data['newCode']);
        if (strlen($newCode) < 3 || strlen($newCode) > 12) {
            $this->error(__('新登录码的字符长度必须为3-12'));
        }

        // 检查登录方式
        $this->checkLoginMethod(['name_code', 'number_code'], '不支持修改', true);

        if ($this->memberData->code != md5($oldCode)) {
            $this->error(__('旧登录码不对'));
        }

        // 唯一性校验
        $memberIds = Member::where(['name' => $this->memberData->name, 'code' => md5($newCode)])
            ->column('id');
        if ($memberIds && array_diff($memberIds, [$this->memberId])) {
            $this->error(__('登录码过于简单，请重新设置'));
        }

        $this->memberData->code = md5($newCode);
        $this->memberData->save();
        MemberLog::quickLog(MemberLog::ACTION_CHANGE_CODE, $this->memberId);  // 记录日志

        $this->success();
    }

    /**
     * 修改手机号
     */
    public function changePhone()
    {
        $data = $this->request->post();
        $validate = new Validate(['phone' => 'require', 'captcha' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $newPhone = trim($data['phone']);

        // 检查登录方式
        $this->checkLoginMethod('phone_captcha', '不支持修改', true);

        // 校验验证码
        $smsCodeList = SmsCode::where([
            'phone' => $newPhone,
            'code'  => trim($data['captcha'])
        ])->order(['created_at' => SORT_DESC])->limit(1)->select();
        if (count($smsCodeList) == 0) {
            $this->error(__('验证码错误'));
        }
        $smsCode = $smsCodeList[0];
        if ($smsCode->expired_at < time() || $smsCode->status != SmsCode::STATE_ING) {
            $this->error(__('验证码失效'));
        }
        $smsCode->status = SmsCode::STATE_END;
        $smsCode->save();

        if ($this->memberData->phone == $newPhone) {
            $this->error(__('不能与当前手机号相同'));
        }

        // 唯一性校验
        $memberIds = Member::where(['phone' => $newPhone])->column('id');
        if ($memberIds && array_diff($memberIds, [$this->memberId])) {
            $this->error(__('手机号已被占用，请重新换一个试试'));
        }

        // 修改手机号
        $this->memberData->phone = $newPhone;
        $this->memberData->save();
        MemberLog::quickLog(MemberLog::ACTION_CHANGE_PHONE, $this->memberId);  // 记录日志

        $this->success();
    }

    /**
     * 修改手机号 - 授权微信手机号
     */
    public function changeWechatPhone()
    {
        $data = $this->request->post();
        $validate = new Validate(['encrypted_data' => 'require', 'iv' => 'require', 'wx_code' => 'require']);
        if (! $validate->check($data)) {
            $this->error($validate->getError());
        }
        $encryptedData = $this->request->post("encrypted_data", '', 'trim');;
        $iv = $data['iv'];
        $wxCode = $data['wx_code'];

        // 检查登录方式
        $this->checkLoginMethod('wechat_phone', '不支持修改', true);

        // 获取微信code
        if (! $wxCodeData = Logic::getMiniCodeData($wxCode)) {
            $this->error(__('获取微信登录信息失败'));
        }

        // 解密手机号
        if ($phoneCode = ($data['code'] ?? null)) {
            if (! $result = Logic::getWechatPhoneData($phoneCode)) {
                $this->error(__('获取微信手机号失败'));
            }
        } else {
            if (! $result = Logic::decryptWechatData($wxCodeData['session_key'], $encryptedData, $iv)) {
                $this->error(__('获取微信手机号失败'));
            }
        }

        $newPhone = $result['phoneNumber'];
        if ($this->memberData->phone == $newPhone) {
            $this->error(__('不能与当前手机号相同'));
        }

        // 唯一性校验
        $memberIds = Member::where(['phone' => $newPhone])->column('id');
        if ($memberIds && array_diff($memberIds, [$this->memberId])) {
            $this->error(__('手机号已被占用，请重新换一个试试'));
        }

        // 修改手机号
        $this->memberData->phone = $newPhone;
        $this->memberData->save();
        MemberLog::quickLog(MemberLog::ACTION_CHANGE_PHONE, $this->memberId);  // 记录日志

        $this->success();
    }


    /**
     * 检查登录方式
     *
     * @param $loginMethod
     */
    private function checkLoginMethod($loginMethod, $tips = '不支持当前登录方式', $checkActivity = false)
    {
        if (is_string($loginMethod)) {
            $loginMethod = [$loginMethod];
        }
        $loginMethods = Config::getGroupAttribute(Config::GROUP_BASE, 'login_methods');
        if ($checkActivity) {
            if ($this->activityData->login_methods) {
                $loginMethods = $this->activityData->login_methods;
            }
        }
        if (! $loginMethods) {
            $this->error(__($tips));
        }
        $loginMethods = explode(',', $loginMethods);
        if (! array_intersect($loginMethods, $loginMethod)) {
            $this->error(__($tips));
        }
    }

    /**
     * 检查登录code
     *
     * @param $wxCode
     */
    private function checkWxCode($wxCode)
    {
        if (Config::getGroupAttribute(Config::GROUP_BASE, 'only_wechat')) {
            if (! $wxCode) {
                $this->error(__('微信登录code不能为空'));
            }
        }
        if ($wxCode) {
            if (! $wxCodeData = Logic::getMiniCodeData($wxCode, false)) {
                $this->error(__('获取微信登录信息失败'));
            }

            return $wxCodeData;
        }

        return null;
    }

    // 尝试授权信息登录
    private function tryUserAuthLogin($openid, $source)
    {
        if ($userId = Logic::getUserAuthUid($openid, $source)) {
            $member = Member::where(['user_id' => $userId])->show()->order('login_time desc')->find();
            if ($member) {
                if (! $activity = Activity::getCurrent($member, true)) {
                    Member::update(['user_id' => 0], ['id' => $member->id]);
                    $this->success('活动已结束或没有参与权限', null, Constant::INVALID_ACTIVITY);
                }
                if ($this->auth->direct($member->user_id)) {
                    MemberLog::quickLog(MemberLog::ACTION_WECHAT_AUTO_LOGIN, $member->id);  // 记录日志
                    $this->success('登陆成功', array_merge($this->auth->getUserInfo(),
                        ['bio' => $this->auth->bio, 'activity_id' => $activity->id]));
                } else {
                    Member::update(['user_id' => 0], ['user_id' => $member->user_id]);
                    $this->success('登陆失败', null, Constant::IGNORE_ERROR);
                }
            }
        }
    }

    /**
     * 执行登录操作
     */
    private function tryMemberLogin($member, $openid, $source, $loginMethod = null)
    {
        if ($member->status != Member::STATUS_NORMAL) {
            $this->error(__('当前用户被禁用，请联系管理员处理'));
        }

        if (! $activity = Activity::getCurrent($member)) {
            $this->error(__('活动已结束或没有参与权限'));
        }
        if ($activity->login_methods && strpos($activity->login_methods, $loginMethod) === false) {  // 校验登录方式
            $this->error(__('不支持当前登录方式'));
        }

        // 会员绑定
        if (! $member->user_id) {
            if ($userId = Logic::getUserAuthUid($openid, $source)) {
                $member->user_id = $userId;
            } else {
                // 注册会员
                $username = 'yungift_m' . str_pad($member->id, 8, '0', STR_PAD_LEFT);
                if ($user = UserModel::get(['username' => $username])) {
                    $member->user_id = $user->id;
                } else {
                    $password = str_pad($member->id, 8, '0', STR_PAD_LEFT);
                    $result = $this->auth->register($username, $password);
                    if (! $result) {
                        $this->error($this->auth->getError());
                    }
                    $member->user_id = $this->auth->id;
                }
            }
        } else {
            if (! UserModel::get(['id' => $member->user_id])) {
                Member::update(['user_id' => 0], ['user_id' => $member->user_id]);
                $this->error(__('登录失败，请稍后重试'));
            }
        }

        $ret = $this->auth->direct($member->user_id);
        if ($ret) {
            $user = $this->auth->getUser();
            if (! $member->avatar_url) {
                $member->avatar_url = $user->getData('avatar');
            }
            $member->login_aid = $activity->id;
            $member->login_time = time();
            $member->save();
            Logic::bindUserAuth($member->user_id, $openid, $source);

            // 记录日志
            $logType = null;
            if ($loginMethod === 'name_code') {
                $logType = MemberLog::ACTION_NAME_CODE_LOGIN;
            } elseif ($loginMethod === 'number_code') {
                $logType = MemberLog::ACTION_NUMBER_CODE_LOGIN;
            } elseif ($loginMethod === 'phone_captcha') {
                $logType = MemberLog::ACTION_PHONE_CAPTCHA_LOGIN;
            } elseif ($loginMethod === 'wechat_phone') {
                $logType = MemberLog::ACTION_WECHAT_PHONE_LOGIN;
            }
            if ($logType) {
                MemberLog::quickLog($logType, $member->id);
            }

            $this->success(__('登录成功'),
                array_merge($this->auth->getUserInfo(), ['bio' => $this->auth->bio, 'activity_id' => $activity->id]));
        } else {
            $this->error($this->auth->getError());
        }
    }

}
